package my.fra;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * <p>分数类，主要用于小数与分数的转换，已经带分数与假分数之间的转换，同时支持分数之间的运算(分数之间的运
 * 算过程是通过@see BigInteger来实现的，所以能够高精度的得到运算结果，中间不会有如何数据丢失)</p>
 * <p>带分数表现形式:{@code 1/2}或者{@code (1)1/2};</p>
 * <p>假分数表现形式:{@code 1/2}或者{@code 3/2},默认为假分数表现形式，同时计算时将自动转换为假
 * 分数形式;</p>
 * 
 * @version 1.2
 * @see     Fraction
 * @since	JDK1.7
 * @author	oyz
 */
public class Fraction extends Number implements Comparable<Fraction>{
	/**
	 * 
	 */
	private static final long serialVersionUID = 656704244793375578L;
	/**
	 * 符号位。当值为0时，分子为1，符号位为0；当值大于0时，符号位为1；当值小于0时，符号位为-1
	 */
	private final int signum;
	/**
	 * 分子。
	 */
	private final BigInteger numerator;
	/**
	 * 分母。分母不能为0，若分母为0，则抛出除0错误
	 */
	private final BigInteger denominator;
	/**
	 * 带分数标志位。当为true时，表现为带分数形式，否则为假分数形式
	 */
	private boolean isMixedNumber;
	
	/**
	 * 精度
	 */
	private int scale;
	
	/**
	 * 舍入规则
	 */
	private int roundingMode;
	
	/**
	 * 0
	 */
	public static final Fraction ZERO=new Fraction(0,0,0,1,true);
	
	/**
	 * 1/2
	 */
	public static final Fraction HALF=new Fraction(1,0,1,2,true);
	
	/**
	 * 1
	 */
	public static final Fraction ONE=new Fraction(1,0,1,1,true);
	
	/**
	 * 2
	 */
	public static final Fraction TWO=new Fraction(1,0,2,1,true);
	
	/**
	 * 10
	 */
	public static final Fraction TEN=new Fraction(1,0,10,1,true);
	
	
	//构造函数/////////////////////////////////////////////
	/**
	 * 构造函数
	 * <p>给所有属性赋值</p>
	 * @param signum 符号位。当值为0时，其他属性无效；当值大于0时，符号位为1；当值小于0时，符号位为-1
	 * @param antilogarithm 真数位。默认为0，转换为带分数时才会用到
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 * @param isMixedNumber 带分数标志位。当为true时，表现为带分数形式，否则为假分数形式
	 */
	public Fraction(int signum,BigInteger antilogarithm,BigInteger numerator,
			BigInteger denominator,boolean isMixedNumber) {
		
		roundingMode=BigDecimal.ROUND_UP;
		scale=6;
		
		signum = signum==0 ? 0 : (signum>0?1:-1);
		
		if(denominator.signum()==0)throw new ArithmeticException();
		
		numerator=antilogarithm.multiply(denominator).add(numerator);
		
		if(signum!=0 && numerator.signum()!=0){
			signum *= numerator.signum()*denominator.signum();
			BigInteger value=GCD(numerator.abs(), denominator.abs());
			
			this.numerator=numerator.abs().divide(value);
			this.denominator=denominator.abs().divide(value);
		}else{
			signum=0;
			this.numerator=BigInteger.ZERO;
			this.denominator=BigInteger.ONE;
		}
		
		this.signum=signum;
		this.isMixedNumber=isMixedNumber;
	}
	
	
	/**
	 * 构造函数
	 * <p>给所有属性赋值</p>
	 * @param signum 符号位。当值为0时，其他属性无效；当值大于0时，符号位为1；当值小于0时，符号位为-1
	 * @param antilogarithm 真数位。默认为0，转换为带分数时才会用到
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 * @param isMixedNumber 带分数标志位。当为true时，表现为带分数形式，否则为假分数形式
	 */
	public Fraction(int signum,Long antilogarithm,Long numerator,
			Long denominator,boolean isMixedNumber) {
		this(signum, new BigInteger(antilogarithm.toString()),
				new BigInteger(numerator.toString()), new BigInteger(denominator.toString()),
				isMixedNumber);
	}
	
	/**
	 * 构造函数
	 * <p>给真数位，分子以及分母属性赋值,此时默认表现形式为带分数</p>
	 * @param antilogarithm 真数位。默认为0，转换为带分数时才会用到
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 */
	public Fraction(Long antilogarithm,Long numerator,Long denominator) {
		this(1, new BigInteger(antilogarithm.toString()),
				new BigInteger(numerator.toString()), new BigInteger(denominator.toString()),
				true);
	}
	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,此时默认表现形式为假分数</p>
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 */
	public Fraction(Long numerator,Long denominator) {
		this(1,0l,numerator, denominator,	false);
	}
	
	/**
	 * 构造函数
	 * <p>给所有属性赋值</p>
	 * @param signum 符号位。当值为0时，分子为1，符号位为0；当值大于0时，符号位为1；当值小于0时，符号位为-1
	 * @param antilogarithm 真数位。默认为0，转换为带分数时才会用到
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 * @param isMixedNumber 带分数标志位。当为true时，表现为带分数形式，否则为假分数形式
	 */
	public Fraction(int signum,Integer antilogarithm,Integer numerator,
			Integer denominator,boolean isMixedNumber) {
		this(signum, new BigInteger(antilogarithm.toString()),
				new BigInteger(numerator.toString()), new BigInteger(denominator.toString()),
				isMixedNumber);
	}
	
	/**
	 * 构造函数
	 * <p>给真数位，分子以及分母属性赋值,此时默认表现形式为带分数</p>
	 * @param antilogarithm 真数位。默认为0，转换为带分数时才会用到
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 */
	public Fraction(Integer antilogarithm,Integer numerator,Integer denominator) {
		this(1, new BigInteger(antilogarithm.toString()),
				new BigInteger(numerator.toString()), new BigInteger(denominator.toString()),
				true);
	}
	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,此时默认表现形式为假分数</p>
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 */
	public Fraction(Integer numerator,Integer denominator) {
		this(1,0,numerator, denominator,	false);
	}
	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,此时默认表现形式为假分数</p>
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 */
	public Fraction(BigInteger numerator,BigInteger denominator) {
		this(1,BigInteger.ZERO,	numerator, denominator,	false);
	}	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,此时默认表现形式为假分数</p>
	 * @param signum 符号位。当值为0时，分子为1，符号位为0；当值大于0时，符号位为1；当值小于0时，符号位为-1
	 * @param numerator 分子。
	 * @param denominator 分母。分母不能为0，若分母为0，则抛出除0错误
	 */
	private Fraction(int signum, BigInteger numerator,BigInteger denominator) {
		this(signum,BigInteger.ZERO,numerator,denominator,false);
	}
	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,由于分数为整数,此时默认表现形式为带分数</p>
	 * @param value 分数的值
	 */
	public Fraction(BigInteger value){
		this(1,BigInteger.ZERO,	value, BigInteger.ONE, true);
	}
	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,由于分数为整数,此时默认表现形式为带分数</p>
	 * @param value 分数的值
	 */
	public Fraction(Integer value){
		this(1,0,value, 1, true);
	}
	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,由于分数为整数,此时默认表现形式为带分数</p>
	 * @param value 分数的值
	 */
	public Fraction(Long value){
		this(1,0l,value, 1l, true);
	}
	
	/**
	 * 构造函数
	 * <p>给相应的属性赋值,由于分数为整数,此时默认表现形式为带分数</p>
	 * @param value 分数的值
	 */
	
	public Fraction(Double value){
		this(new BigInteger(value.toString().replace(".", "")),
				BigInteger.TEN.pow(getPointIndex(value.toString())));
	}
	
	/**
	 * 默认构造函数
	 * <p>值为0</p>
	 */
	public Fraction() {
		this(0,0l,1l,1l,false);
	}
	

	//实用功能/////////////////////////////////////////////////////////
	/**四则运算********************************************************/
	/**
	 * 绝对值
	 * @return {@code |this|}
	 */
	public Fraction abs(){
		return new Fraction(1,numerator,denominator);
	}
	
	/**
	 * 相反数
	 * @return 返回{@code this}的相反数
	 */
	 public Fraction opposite(){
		 return new Fraction(-signum,numerator,denominator);
	 }
	
	 /**
	  * 倒数
	  * @return 返回{@code this} 的倒数
	  */
	 public Fraction reciprocal(){
		 return new Fraction(signum,denominator,numerator);
	 }
	 
	 /**
	  * 乘方
	  * @param exponent
	  * @return {@code this ^ exponent}
	  */
	 public Fraction pow(int exponent){
		 return new Fraction(signum<0 && exponent%2==1 ? -1 :1,numerator.pow(exponent),denominator.pow(exponent));
	 }
	 
	/**
	 * 加法运算
	 * @param fra
	 * @return {@code this + fra}
	 */
	public Fraction add(Fraction fra){
		if(fra.signum!=signum)return subtract(fra.opposite());
		return new Fraction(signum,numerator.multiply(fra.denominator).
				add(fra.numerator.multiply(denominator)),
				denominator.multiply(fra.denominator));
	}
	
	/**
	 * 减法运算
	 * @param fra
	 * @return {@code this - fra}
	 */
	public Fraction subtract(Fraction fra){
		if(fra.signum!=signum)return add(fra.opposite());
		BigInteger b1=numerator.multiply(fra.denominator).
				subtract(fra.numerator.multiply(denominator));
		if(b1.signum()<0)return new Fraction(fra.signum,b1,denominator.multiply(fra.denominator));
		return new Fraction(signum,b1,denominator.multiply(fra.denominator));
	}
	
	/**
	 * 乘法运算
	 * @param fra
	 * @return {@code this * fra}
	 */
	public Fraction multiply(Fraction fra){
		return new Fraction(signum*fra.signum,numerator.multiply(fra.numerator),denominator.multiply(fra.denominator));
	}
	
	/**
	 * 除法运算
	 * @param fra
	 * @return {@code this / fra}
	 */
	public Fraction divide(Fraction fra){
		return multiply(fra.reciprocal());
	}
	
	/**重写函数**************************************************/
	/**
	 * 重载toString方法
	 */
	@Override
	public String toString() {
		StringBuilder sb=new StringBuilder();
		if(getSignum()==0)sb.append("0");
		else{
			if(getSignum()<0)	sb.append("-");
			BigInteger antilogarithm=getIntegerPart();
			if(antilogarithm.signum()>0){
				sb.append("(");sb.append(antilogarithm);sb.append(")");
			}
			BigInteger numerator0=getNumerator();
			if(numerator0.signum()>0){
				sb.append(numerator0);
				if(getDenominator().subtract(new BigInteger("0")).signum()>0){sb.append("/");sb.append(getDenominator());}
			}
		}		
		return sb.toString();
	}
	
	/**
	 * 获得小数形式
	 * @return 返回小数小数
	 */
	public String toDecimalString(){
		return getValue().toString();
	}
	
	/**
	 * 比较智能的显示分数,当为整数时直接显示为整数
	 * @return 返回字符串
	 */
	public String toAutoString(){
		if(signum==0)return "0";
		StringBuilder sb=new StringBuilder();
		if(signum<0)sb.append("-");
		
		if(numerator.signum()!=0 && 
				denominator.compareTo(new BigInteger("1"))==0){
			sb.append(numerator);
		}else{
			BigInteger intPart=getIntegerPart();
			BigInteger numerator=getNumerator();
			if(intPart.signum()!=0){
				sb.append('(');
				sb.append(intPart);
				sb.append(')');
			}
			sb.append(numerator);
			sb.append('/');
			sb.append(denominator);
		}
		
		return sb.toString();
	}
	
	/**
	 * 重载equals函数
	 */
	@Override
	public boolean equals(Object x) {
		if (x == this)
            return true;

        if (!(x instanceof Fraction))
            return false;

       Fraction fra=(Fraction) x;
       if(fra.signum!=signum || 
    		   (!fra.numerator.equals(numerator)) ||
    		   (!fra.denominator.equals(denominator))
    		)return false;
       
       return true;
	}
	
	/**
	 * 重载hashCode函数，哈希值为toString()的字符串的哈希值
	 */
	@Override
	public int hashCode() {
		return toString().hashCode();
	}
	
	//静态方法//////////////////////////////////////////////////////////
	/**
	 * 获得一个数的符号位
	 * @param value
	 * @return {@code value.signum}
	 */
	public static<Type extends Number> int getSignum(Type value){
		if(value==null)return 0;
		String str=value.toString();
		char f=str.charAt(0);
		if(str.length()==1&&f=='0')return 0;
		if(f=='-')return -1;
		return 1;
	}
	/**
	 * 将value与0比较大小
	 * @param value
	 * @return {@code value.compareTo(0)}
	 */
	private static int compareZero(BigInteger value){
		return value.compareTo(BigInteger.ZERO);
	}
	/**
	 * 获得小数点的位置
	 * @param str
	 * @return 返回小数点的索引
	 */
	public static int getPointIndex(String str){
		if(!str.contains("."))str+='.';
		return str.length()-str.indexOf('.')-1;
	}
	/**
	 * 获得最大公约数
	 * @param value1
	 * @param value2
	 * @return 返回value1和vlaue2的最大公约数
	 */
	public static BigInteger GCD(BigInteger value1, BigInteger value2) {
		if(compareZero(value1)==0)return BigInteger.ZERO;
		if(compareZero(value2)==0)return value1;
		return GCD(value2,value1.mod(value2));
	}
	/**
	 * 获得最小公倍数
	 * @param value1
	 * @param value2
	 * @return 返回value1和value2的最小公倍数
	 */
	public static BigInteger LCM(BigInteger value1, BigInteger value2){
		return value1.divide(GCD(value1,value2)).multiply(value2);
	}
	/**
	 * <p>将字符串转换为分数类</p>
	 * <p>转换的字符串格式有:小数,整数,带分数,假分数</p>
	 * @param valuestr
	 * @return 返回之后的Fraction实例对象
	 */
	public static Fraction parseFraction(String valuestr){
		if(valuestr==null||valuestr.trim().length()==0){
			throw new NullPointerException();
		}
		
		int signum=1;
		if(valuestr.charAt(0)=='-'){
			valuestr = valuestr.substring(1);
			signum=-1;
		}
		
		String regex1="^([(][0-9]*[.]{0,1}[0-9]*[)]){0,1}[0-9]*[.]{0,1}[0-9]*[/]{0,1}[0-9]*[.]{0,1}[0-9]*$";
		String regex2="([0-9]*[.]{0,1}[0-9]*)";
		
		if(!valuestr.matches(regex1)){
			throw new NumberFormatException();
		}
		
		Pattern p = Pattern.compile(regex2);
		Matcher m=p.matcher(valuestr);
		List<String> li_str=new ArrayList<>();
		while(m.find()){
			String s=m.group();
			if(s.trim().length()!=0){
				li_str.add(s=="."?"0":s);
			}
		}
		try {
			if(li_str.size()==1){
				String str=li_str.get(0);
				BigInteger v1=new BigInteger(str.replace(".", ""));
				BigInteger v2=BigInteger.TEN.pow(getPointIndex(str));
				return new Fraction(signum,v1,v2);
			}else if(li_str.size()==2){
				String str1=li_str.get(0);
				BigInteger v1=new BigInteger(str1.replace(".", ""));
				BigInteger v2=BigInteger.TEN.pow(getPointIndex(str1));
				
				String str2=li_str.get(1);
				BigInteger v3=new BigInteger(str2.replace(".", ""));
				BigInteger v4=BigInteger.TEN.pow(getPointIndex(str2));
				return new Fraction(signum,v1.multiply(v4),v2.multiply(v3));
			}else{
				String str1=li_str.get(0);
				String str2=li_str.get(1);
				String str3=li_str.get(2);
				
				BigDecimal d2=new BigDecimal(str3);
				BigDecimal d1=new BigDecimal(str1).multiply(d2).
						add(new BigDecimal(str2));
				
				String str4=d1.toString();
				BigInteger v1=new BigInteger(str4.replace(".", ""));
				BigInteger v2=BigInteger.TEN.pow(getPointIndex(str4));
				
				String str5=d2.toString();
				BigInteger v3=new BigInteger(str5.replace(".", ""));
				BigInteger v4=BigInteger.TEN.pow(getPointIndex(str5));
				return new Fraction(signum,v1.multiply(v4),v2.multiply(v3));
			}
		} catch (Exception e) {
			throw new RuntimeException("转换时出错，可能是数据太大导致内存溢出");
		}
	}
	
	//get和set方法////////////////////////////////////////////
	
	
	/**
	 * 获得精度
	 * @return {@code scale}
	 */
	public int getScale() {
		return scale;
	}
	
	/**
	 * 设置精度
	 * @param scale
	 */
	public Fraction setScale(int scale) {
		this.scale = scale;
		return this;
	}
	
	/**
	 * 获得舍入规则
	 * @return {@code roundingMode}
	 */
	public int getRoundingMode() {
		return roundingMode;
	}
	
	/**
	 * 设置舍入规则
	 * @param roundingMode
	 */
	public Fraction setRoundingMode(int roundingMode) {
		this.roundingMode = roundingMode;
		return this;
	}
	
	/**
	 * 是否为带分数，默认为为false
	 * @return {@code true|false}
	 */
	public boolean isMixedNumber() {
		return isMixedNumber;
	}


	/**
	 * 设置分数显示形式，true为带分数，false为假分数
	 * @param isMixedNumber
	 */
	public Fraction setMixedNumber(boolean isMixedNumber) {
		this.isMixedNumber = isMixedNumber;
		return this;
	}
	
	/**
	 * 设置符号位
	 * @return {@code signum}
	 */
	public int getSignum() {
		return signum;
	}
	
	/**
	 * 获得分数的整数部分，假分数时为0
	 * @return {@code numerator/denominator}
	 */
	public BigInteger getIntegerPart() {
		if(signum==0 || !isMixedNumber())return BigInteger.ZERO;
		return numerator.divide(denominator);
	}
	
	/**
	 * 获得分子
	 * @return {@code numerator|(numerator%denominator)}
	 */
	public BigInteger getNumerator() {
		if(isMixedNumber())return numerator.mod(denominator);
		return numerator;
	}
	
	/**
	 * 获得分母
	 * @return {@code denominator}
	 */
	public BigInteger getDenominator() {
		return denominator;
	}
	
	/**
	 * 获得分数的值，
	 * @return 获得分数形式
	 */
	public BigDecimal getValue(){
		return new BigDecimal(numerator.multiply(
				new BigInteger(Integer.toString(signum)))).
				divide(new BigDecimal(denominator),getScale(),
						getRoundingMode());
	}
	
	/**
	 * 获得分数的值，
	 * @return 获得分数形式
	 */
	public BigDecimal getValue(int scale,int roundingMode){
		return new BigDecimal(numerator.multiply(
				new BigInteger(Integer.toString(signum)))).
				divide(new BigDecimal(denominator),scale,
						roundingMode);
	}
	/*实现函数********************************************/
	/**
	 * 实现比较函数
	 */

	@Override
	public int compareTo(Fraction o) {
		return subtract(o).signum;
	}

	/**
	 * 转换为整数
	 */
	@Override
	public int intValue() {
		return getValue().intValue();
	}

	/**
	 * 转换为长整形
	 */
	@Override
	public long longValue() {
		return getValue().longValue();
	}

	/**
	 * 转换为浮点型
	 */
	@Override
	public float floatValue() {
		return getValue().floatValue();
	}

	/**
	 * 转换为双精度浮点型
	 */
	@Override
	public double doubleValue() {
		return getValue().doubleValue();
	}

}
